From 7700ec47f17c2f8b76b33d79b62729009a020d5d Mon Sep 17 00:00:00 2001 From: "tw275@labyrinth.cl.cam.ac.uk" Date: Wed, 28 Jul 2004 12:13:47 +0000 Subject: [PATCH] bitkeeper revision 1.1108.29.1 (4107987b3YS7DchKHz1bWQKqXsXTsg) Added xensv command: xensv start / stop - checks for twisted and for xend More work on wizzard, now persits values and can move back / forth --- .rootkeys | 3 + BitKeeper/etc/ignore | 11 +++ tools/misc/Makefile | 2 +- tools/misc/xensv | 134 ++++++++++++++++++++++++++++ tools/python/xen/sv/CreateDomain.py | 19 ++-- tools/python/xen/sv/Daemon.py | 108 ++++++++++++++++++++++ tools/python/xen/sv/HTMLBase.py | 2 +- tools/python/xen/sv/Wizzard.py | 78 ++++++++++++---- tools/python/xen/sv/params.py | 3 + tools/python/xen/sv/util.py | 27 +++++- 10 files changed, 360 insertions(+), 27 deletions(-) create mode 100755 tools/misc/xensv create mode 100644 tools/python/xen/sv/Daemon.py create mode 100644 tools/python/xen/sv/params.py diff --git a/.rootkeys b/.rootkeys index d09ef0e183..671bb2c25e 100644 --- a/.rootkeys +++ b/.rootkeys @@ -310,6 +310,7 @@ 3f870808zS6T6iFhqYPGelroZlVfGQ tools/misc/xen_cpuperf.c 405eedf6_nnNhFQ1I85lhCkLK6jFGA tools/misc/xencons 40c9c4697z76HDfkCLdMhmaEwzFoNQ tools/misc/xend +4107986eMWVdBoz4tXYoOscpN_BCYg tools/misc/xensv 4056f5155QYZdsk-1fLdjsZPFTnlhg tools/misc/xensymoops.py 40cf2937dqM1jWW87O5OoOYND8leuA tools/misc/xm 40c9c468icGyC5RAF1bRKsCXPDCvsA tools/python/Makefile @@ -372,6 +373,7 @@ 40dc4076pVeE1kEEWzcUaNZin65kCA tools/python/xen/lowlevel/xu/domain_controller.h 40dc4076CwBYRTUQDdbdU1L6KcLgSw tools/python/xen/lowlevel/xu/xu.c 41052eb84_irpx0E9N_kqBp9eoin5g tools/python/xen/sv/CreateDomain.py +4107986egkTAMIHW7n-i4ShvCGWpLQ tools/python/xen/sv/Daemon.py 40fcefb2qm13BbRZBydAatOavaS0fQ tools/python/xen/sv/DomInfo.py 40fcefb2-RIU8GB67mJMRzybME9bxw tools/python/xen/sv/DomList.py 40fcefb23FfQn-ZBCbcHqA0cPGqQxw tools/python/xen/sv/GenTabbed.py @@ -381,6 +383,7 @@ 40fcefb2Sif__6AqrANeBQZZfvP-6w tools/python/xen/sv/TabView.py 41052eb8UrgtUkuJPg7oY1tutVQHsg tools/python/xen/sv/Wizzard.py 40fcefb2DqteqCCZYDCvvh4Q5jBd0w tools/python/xen/sv/__init__.py +4107986e6qN1IdvIDdId0AYFmDMkiQ tools/python/xen/sv/params.py 40fcefb4rnaZNjqsBu7A5V2rlLyqRw tools/python/xen/sv/util.py 40d8915cyoVA0hJxiBFNymL7YvDaRg tools/python/xen/util/Brctl.py 40dfd40aGqGkiopOOgJxSF4iCbHM0Q tools/python/xen/util/__init__.py diff --git a/BitKeeper/etc/ignore b/BitKeeper/etc/ignore index 60cc1f1a44..46e5aff702 100644 --- a/BitKeeper/etc/ignore +++ b/BitKeeper/etc/ignore @@ -35,3 +35,14 @@ xen/xen.* tools/xfrd/xfrd xen/tools/elf-reloc xen/tools/figlet/figlet +docs/interface.aux +docs/interface.log +docs/interface.pdf +docs/interface.ps +docs/interface.toc +docs/user.aux +docs/user.log +docs/user.pdf +docs/user.ps +docs/user.toc +tools/web-shutdown.tap diff --git a/tools/misc/Makefile b/tools/misc/Makefile index 9795997364..2f613ea172 100644 --- a/tools/misc/Makefile +++ b/tools/misc/Makefile @@ -21,7 +21,7 @@ OBJS = $(patsubst %.c,%.o,$(SRCS)) TARGETS = xen_cpuperf INSTALL_BIN = $(TARGETS) xencons -INSTALL_SBIN = netfix xm xend +INSTALL_SBIN = netfix xm xend xensv all: $(TARGETS) $(MAKE) -C miniterm diff --git a/tools/misc/xensv b/tools/misc/xensv new file mode 100755 index 0000000000..b877538d67 --- /dev/null +++ b/tools/misc/xensv @@ -0,0 +1,134 @@ +#!/usr/bin/env python +# -*- mode: python; -*- +#============================================================================ +# Copyright (C) 2004 Tom Wilkie +# Copyright (C) 2004 Mike Wray +#============================================================================ + +"""SV web interface Lives in /usr/sbin. + Provides pretty HTML management interface. + + Run: + + sv start + + The daemon is stopped with: + + sv stop + + The daemon will be accessible from http://localhost:8080/ +""" +import os +import sys +import re + +from xen.xend.server.params import PID_FILE as XEND_PID_FILE + +class CheckError(ValueError): + pass + +def hline(): + print >>sys.stderr, "*" * 70 + +def msg(message): + print >>sys.stderr, "*" * 3, message + +def check_logging(): + """Check python logging is installed and raise an error if not. + Logging is standard from Python 2.3 on. + """ + try: + import logging + except ImportError: + hline() + msg("Python logging is not installed.") + msg("Use 'make install-logging' at the xen root to install.") + msg("") + msg("Alternatively download and install from") + msg("http://www.red-dove.com/python_logging.html") + hline() + raise CheckError("logging is not installed") + +def check_twisted_version(): + """Check twisted is installed with a supported version and print a warning if not. + Raises an error if twisted is not installed. + """ + # Supported twisted release and major version. + RELEASE = 1 + MAJOR = 3 + try: + from twisted.copyright import version + except ImportError: + hline() + msg("The Twisted framework is not installed.") + msg("Use 'make install-twisted' at the xen root to install.") + msg("") + msg("Alternatively download and install version %d.%d or higher" % (RELEASE, MAJOR)) + msg("from http://www.twistedmatrix.com/products") + hline() + raise CheckError("twisted is not installed") + + + (release, major, minor) = version.split('.') + release = int(release) + major = int(major) + if release > RELEASE: return + if release == RELEASE and major >= MAJOR: return + hline() + msg("Warning: Twisted version not supported: %s" % version) + msg("Use Twisted version %d.%d.0 or higher" % (RELEASE, MAJOR)) + hline() + +def check_xend(): + """Check xend is running + """ + + if not os.path.isfile(XEND_PID_FILE) or not os.path.getsize(XEND_PID_FILE): + hline() + msg( "Warning: Xend has not been detected as running." ) + msg( "Please start it immediately with: xend start " ) + hline() + return 0 + + # Read the pid of the previous invocation and search active process list. + pid = open(XEND_PID_FILE, 'r').read() + lines = os.popen('ps ' + pid + ' 2>/dev/null').readlines() + for line in lines: + if re.search('^ *' + pid + '.+xend', line): + return 1 + + hline() + msg( "Warning: Xend has not been detected as running." ) + msg( "Please start it immediately with: xend start " ) + hline() + return 0 + +def main(): + try: + check_logging() + check_twisted_version() + check_xend() + except CheckError: + sys.exit(1) + + from xen.sv import Daemon + + daemon = Daemon.instance() + + if not sys.argv[1:]: + print 'usage: %s {start|stop|restart}' % sys.argv[0] + elif os.fork(): + pid, status = os.wait() + return status >> 8 + elif sys.argv[1] == 'start': + return daemon.start() + elif sys.argv[1] == 'stop': + return daemon.stop() + elif sys.argv[1] == 'restart': + return daemon.stop() or daemon.start() + else: + print 'not an option:', sys.argv[1] + return 1 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tools/python/xen/sv/CreateDomain.py b/tools/python/xen/sv/CreateDomain.py index 13c45d8622..62c6fe9d5b 100644 --- a/tools/python/xen/sv/CreateDomain.py +++ b/tools/python/xen/sv/CreateDomain.py @@ -14,30 +14,33 @@ class CreatePage0( Sheet ): def __init__( self, urlWriter ): - feilds = [( 'name', 'Name')] + feilds = [( 'name', 'VM Name:'), + ( 'memory', 'RAM (Mb):' )] - Sheet.__init__( self, urlWriter, feilds, "Create New Domain - 1" ) + Sheet.__init__( self, urlWriter, feilds, "Create New Domain", 0 ) class CreatePage1( Sheet ): def __init__( self, urlWriter ): - feilds = [( 'name', 'Name')] + feilds = [( 'kernel_type', 'Kernel Type:'), + ( 'kernel_location', 'Kernel location:')] - Sheet.__init__( self, urlWriter, feilds, "Create New Domain - 2" ) + Sheet.__init__( self, urlWriter, feilds, "Setup Kernel Image", 1 ) class CreatePage2( Sheet ): def __init__( self, urlWriter ): - feilds = [( 'name', 'Name')] + feilds = [( 'vbd_dom0', 'Location of vbd:'), + ( 'vbd_dom0', 'Vitualised location:')] - Sheet.__init__( self, urlWriter, feilds, "Create New Domain - 3" ) + Sheet.__init__( self, urlWriter, feilds, "Setup Virtual Block Devices", 2 ) class CreatePage3( Sheet ): def __init__( self, urlWriter ): - feilds = [( 'name', 'Name')] + feilds = [( 'vifs', 'Number of Vifs:')] - Sheet.__init__( self, urlWriter, feilds, "Create New Domain - 4" ) \ No newline at end of file + Sheet.__init__( self, urlWriter, feilds, "Create New Domain - 4", 3 ) diff --git a/tools/python/xen/sv/Daemon.py b/tools/python/xen/sv/Daemon.py new file mode 100644 index 0000000000..fdbc6145dc --- /dev/null +++ b/tools/python/xen/sv/Daemon.py @@ -0,0 +1,108 @@ +########################################################### +## XenSV Web Control Interface Daemon +## Copyright (C) 2004, K A Fraser (University of Cambridge) +## Copyright (C) 2004, Mike Wray +## Copyright (C) 2004, Tom Wilkie +########################################################### + +import os +import os.path +import sys +import re + +from xen.sv.params import * + +from twisted.internet import reactor +from twisted.web import static, server, script + +class Daemon: + """The xend daemon. + """ + def __init__(self): + self.shutdown = 0 + self.traceon = 0 + + def daemon_pids(self): + pids = [] + pidex = '(?P\d+)' + pythonex = '(?P\S*python\S*)' + cmdex = '(?P.*)' + procre = re.compile('^\s*' + pidex + '\s*' + pythonex + '\s*' + cmdex + '$') + xendre = re.compile('^/usr/sbin/xend\s*(start|restart)\s*.*$') + procs = os.popen('ps -e -o pid,args 2>/dev/null') + for proc in procs: + pm = procre.match(proc) + if not pm: continue + xm = xendre.match(pm.group('cmd')) + if not xm: continue + #print 'pid=', pm.group('pid'), 'cmd=', pm.group('cmd') + pids.append(int(pm.group('pid'))) + return pids + + def new_cleanup(self, kill=0): + err = 0 + pids = self.daemon_pids() + if kill: + for pid in pids: + print "Killing daemon pid=%d" % pid + os.kill(pid, signal.SIGHUP) + elif pids: + err = 1 + print "Daemon already running: ", pids + return err + + def cleanup(self, kill=False): + # No cleanup to do if PID_FILE is empty. + if not os.path.isfile(PID_FILE) or not os.path.getsize(PID_FILE): + return 0 + # Read the pid of the previous invocation and search active process list. + pid = open(PID_FILE, 'r').read() + lines = os.popen('ps ' + pid + ' 2>/dev/null').readlines() + for line in lines: + if re.search('^ *' + pid + '.+xensv', line): + if not kill: + print "Daemon is already running (pid %d)" % int(pid) + return 1 + # Old daemon is still active: terminate it. + os.kill(int(pid), 1) + # Delete the stale PID_FILE. + os.remove(PID_FILE) + return 0 + + def start(self, trace=0): + if self.cleanup(kill=False): + return 1 + + # Fork -- parent writes PID_FILE and exits. + pid = os.fork() + if pid: + # Parent + pidfile = open(PID_FILE, 'w') + pidfile.write(str(pid)) + pidfile.close() + return 0 + # Child + self.run() + return 0 + + def stop(self): + return self.cleanup(kill=True) + + def run(self): + root = static.File( SV_ROOT ) + root.indexNames = [ 'Main.rpy' ] + root.processors = { '.rpy': script.ResourceScript } + reactor.listenTCP( SV_PORT, server.Site( root ) ) + reactor.run() + + def exit(self): + reactor.diconnectAll() + sys.exit(0) + +def instance(): + global inst + try: + inst + except: + inst = Daemon() + return inst diff --git a/tools/python/xen/sv/HTMLBase.py b/tools/python/xen/sv/HTMLBase.py index 7c500e8a2b..07adfab76d 100755 --- a/tools/python/xen/sv/HTMLBase.py +++ b/tools/python/xen/sv/HTMLBase.py @@ -25,9 +25,9 @@ class HTMLBase( Resource ): request.write( 'Xen' ) request.write( '' ) request.write( '' ) + request.write('
' % request.uri) def write_BOTTOM( self, request ): - request.write('' % request.uri) request.write('') request.write('
') request.write( "" ) diff --git a/tools/python/xen/sv/Wizzard.py b/tools/python/xen/sv/Wizzard.py index 0f506d03b8..eaec7592b6 100755 --- a/tools/python/xen/sv/Wizzard.py +++ b/tools/python/xen/sv/Wizzard.py @@ -2,13 +2,14 @@ from xen.sv.util import * from xen.sv.HTMLBase import HTMLBase from xen.xend import sxp +DEBUG = 1 + class Wizzard( HTMLBase ): def __init__( self, urlWriter, title, sheets ): HTMLBase.__init__( self ) self.title = title self.sheets = sheets - self.currSheet = 0 self.urlWriter = urlWriter def write_MENU( self, request ): @@ -22,45 +23,90 @@ class Wizzard( HTMLBase ): currSheet = getVar( 'sheet', request ) if not currSheet is None: + currSheet = int( currSheet ) + else: + currSheet = 0 + + op = getVar( 'op', request ) - self.currSheet = int( currSheet ) + if op == 'next': + currSheet += 1 + elif op == 'prev': + currSheet -= 1 - self.sheets[ self.currSheet ]( self.urlWriter ).write_BODY( request ) + self.sheets[ currSheet ]( self.urlWriter ).write_BODY( request ) request.write( "" ) - request.write( "
" ) - request.write( "

 " ) - request.write( "

" ) - request.write( "

" ) + request.write( "

" ) + if currSheet > 0: + request.write( " " ) + if currSheet < ( len( self.sheets ) - 1 ): + request.write( "" ) + request.write( "

" ) request.write( "" ) + def op_next( self, request ): + pass + + def op_prev( self, request ): + pass + class Sheet( HTMLBase ): - def __init__( self, urlWriter, feilds, title ): + def __init__( self, urlWriter, feilds, title, location ): HTMLBase.__init__( self ) self.urlWriter = urlWriter self.feilds = feilds self.title = title + self.location = location + self.passback = "()" def parseForm( self, request ): - return sxp.toString( request.args ) + do_not_parse = [ 'mod', 'op', 'sheet' ] + + passed_back = request.args + + temp_passback = passed_back.get( "passback" ) + + if temp_passback is not None and len( temp_passback ) > 0: + temp_passback = temp_passback[ len( temp_passback )-1 ] + else: + temp_passback = "(passback )" + + last_passback = ssxp2hash( string2sxp( temp_passback ) ) + + if DEBUG: print last_passback + + try: + del passed_back[ 'passback' ] + except: + pass + + for (key, value) in passed_back.items(): + if key not in do_not_parse: + last_passback[ key ] = value[ len( value ) - 1 ] + + self.passback = sxp2string( hash2sxp( last_passback ) ) #store the sxp + + if DEBUG: print self.passback + + return last_passback #return the hash def write_BODY( self, request ): + request.write( "

%s

" % self.title ) - previous_values = request.args + previous_values = self.parseForm( request ) #get the hash for quick reference for (feild, name) in self.feilds: - value = sxp.child_value( previous_values, feild ) + value = previous_values.get( feild ) if value is None: value = '' request.write( "

%s

" % (name, feild, value) ) - - def op_next( self, request ): - pass - def op_prev( self, request ): - pass + request.write( "

" % self.passback ) + request.write( "

" % self.location ) + diff --git a/tools/python/xen/sv/params.py b/tools/python/xen/sv/params.py new file mode 100644 index 0000000000..16b764f088 --- /dev/null +++ b/tools/python/xen/sv/params.py @@ -0,0 +1,3 @@ +SV_PORT = 8080 +SV_ROOT = "/var/xen/sv/" +PID_FILE = "/var/xen/sv.pid" \ No newline at end of file diff --git a/tools/python/xen/sv/util.py b/tools/python/xen/sv/util.py index adf75b073c..c14137609f 100755 --- a/tools/python/xen/sv/util.py +++ b/tools/python/xen/sv/util.py @@ -18,7 +18,32 @@ def sxp2hash( s ): else: sxphash[ child[0] ] = child[1] - return sxphash + return sxphash + +def ssxp2hash( s ): + sxphash = {} + + for i in s: + if isinstance( i, types.ListType ) and len( i ) > 1: + sxphash[ i[0] ] = i[1] + + return sxphash + +def hash2sxp( h ): + hashsxp = [] + + for (key, item) in h.items(): + hashsxp.append( [key, item] ) + + return hashsxp + +def string2sxp( string ): + pin = sxp.Parser() + pin.input( string ) + return pin.get_val() + +def sxp2string( sexp ): + return sxp.to_string( sexp ) def sxp2prettystring( sxp ): class tmp: -- 2.30.2